<script lang='ts' setup>
import { ref, watch } from 'vue';
import RightMenu from "@/components/RightMenu.vue";

const visible = ref(false)
const props = withDefaults(defineProps<{ dialogVisible: boolean, appId: string }>(), {
  dialogVisible: false,
})
const emits = defineEmits<{
  (e: 'update:dialogVisible', val: boolean): void,
  (e: 'changeData', val: any[]): void,
  (e: 'selectEditPage', val: any): void,
}>()

function cancel() {
  resetEditItem()
  emits('update:dialogVisible', false)
  dataSource.value = []
}

import type Node from 'element-plus/es/components/tree/src/model/node'
import { List, Folder, Operation, } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus';
import router from '@/router';
import { addModule, deleteModule, getAllModule, getModuleList, ModuleInterface, ModuleTreeInterface, updateModule } from '@/api/moduleManage';
import { useDragMethods } from './hooks/useDragMethods';
import { generateRandomString } from '@/utils';
import { Action } from '@/views/moduleDetail/moduleDetailType';

const rules = ref({
  name: [
    { required: true, message: '请输入模型英文名', trigger: 'blur' },
    { pattern: /^[a-zA-Z0-9_-]+$/, message: '只包含字母、数字、_和-', trigger: 'blur' },
  ],
  'data.modelName': [
    { required: true, message: '请选择模型', trigger: 'none' }
  ],
  'data.action': [
    { required: true, message: '请选择方法', trigger: 'none' }
  ],
})
const dataSource = ref<any[]>([])
const isAddOrEdit = ref(1)//编辑还是添加 1为添加，2为编辑

/**
 * 向树形结构的数据中添加一个新的子节点
 *
 * @param data 要添加子节点的树形结构数据
 * @param type 模型类型，1为目录，2为接口，3为模型
 */
function append(data: any | null, type: string = '3') {
  isAddOrEdit.value = 1
  // 打开编辑框
  visibleEditItem.value = true
  if (data) {
    editItem.value.parentId = data.id
  }
  editItem.value.appId = props.appId
  editItem.value.type = type
}

/**
 * 从树形结构中删除指定的节点
 *
 * @param node 要删除的节点对象
 * @param data 节点对应的数据对象
 */
function remove(node: Node, data: any) {
  ElMessageBox.confirm('确定删除该模型吗？', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
    deleteModule(data.id).then(() => {
      ElMessage.success('删除成功')
      getList()
      // 删除后重置选中项
      currentHightLight.value = ''
      currentRightData.value = null
    })
  })
}

const visibleEditItem = ref(false)
const editItem = ref<ModuleInterface>({
  id: '',
  name: '',
  type: '1',
  appId: props.appId,
  parentId: '',
  data: {},
  sort: 0,
})
function resetEditItem() {
  editItem.value = {
    id: '',
    name: '',
    type: '1',
    appId: props.appId,
    parentId: '',
    data: {},
    sort: 0,
  }
  setTimeout(() => { editModuleDialogRef.value && editModuleDialogRef.value.clearValidate() })
}
const currentModuleItemId = ref<number | null>(0)

/**
 * 编辑树形数据项
 *
 * @param data 树形数据项
 */
function edit(data: any) {
  isAddOrEdit.value = 2
  visibleEditItem.value = true
  editItem.value = JSON.parse(JSON.stringify(data))
  currentModuleItemId.value = data.id
}

/**
 * 取消编辑项
 *
 * 将编辑项的显示状态设置为 false，并将编辑项的值重置为一个空的 any 对象。
 */
function cancelEditItem() {
  resetEditItem()
  visibleEditItem.value = false
  currentModuleItemId.value = null
}


const {
  setSort,
  getParentNode,
  resetParentId,
} = useDragMethods()

/**
 * 处理节点拖拽放置的函数
 *
 * @param draggingNode 被拖拽的节点
 * @param parentDropNode 放置节点的父节点
 * @param position 放置位置
 * @param ev 拖拽事件对象
 */
async function handleNodeDrop(draggingNode: any, parentDropNode: any, position: string, ev: DragEvent) {
  // 更新sort
  setSort(dataSource.value)
  console.log(dataSource.value);

  // 获取放置间隙的父节点，最外层为null
  let pId = null;
  for (let i = 0; i < dataSource.value.length; i++) {
    pId = getParentNode(draggingNode.data, dataSource.value[i])
    if (pId) {
      break;
    }
  }
  //更新parentId
  resetParentId(dataSource.value, draggingNode.data.id, pId)

  // 将menuList的数据 全部请求一次
  let updateNodeList = [] as any[]

  /**
   * 深度优先遍历给定列表，并将遍历到的节点添加到 updateNodeList 中
   *
   * @param list 待遍历的列表
   */
  function _dfs(list: any) {
    for (let i = 0; i < list.length; i++) {
      updateNodeList.push({ ...list[i] })
      if (list[i].children && list[i].children.length > 0) {
        _dfs(list[i].children)
      }
    }
  }

  _dfs(dataSource.value)

  let reqList = [] as any[]
  for (let i = 0; i < updateNodeList.length; i++) {
    reqList.push(updateModule(updateNodeList[i]))
  }
  Promise.all(reqList).then(() => {
    getList()
    ElMessage.success('更新成功')
  })
}

/**
 * 提交编辑后的项目
 *
 * @returns 无返回值
 */
const editModuleDialogRef = ref()

function submitEditItem() {
  editModuleDialogRef.value.validate((valid: boolean) => {
    if (valid) {
      visibleEditItem.value = false;
      // 编辑
      if (editItem.value.id) {
        // 更新
        updateModule(editItem.value).then(res => {
          ElMessage.success('修改成功')
          getList()
        })
      } else {
        // 添加
        addModule(editItem.value).then(res => {
          ElMessage.success('添加成功')
          getList()
        })
      }
      // cancelEditItem()
    }
  })
}


//-----------跳转到编辑详情-------------
function editModuleDetail(data: any) {
  router.push({ path: '/moduleDetail', query: { id: data.id } })
}

//-----------跳转到编辑详情结束-------------

/**
 * 获取模型列表数据
 *
 * 从后端获取模块列表，并将获取到的数据赋值给 dataSource.value
 */
function getList() {
  getModuleList({ appId: props.appId }).then(res => {
    dataSource.value = res.data
    getAllModuleList()
  })
}


/**
 * 处理树形组件失去焦点事件
 *
 * @param data 任意类型的数据
 * @param node 树形组件的节点对象
 */
const showRightMenu = ref(false)
function handleTreeBlur(blur: boolean) {
  //如果失焦，则关闭右键菜单
  if (blur) {
    showRightMenu.value = false
  } else {
    setTimeout(() => {
      showRightMenu.value = false
    }, 300)
  }
}

/**
 * 打开右键菜单
 *
 * @param event 事件对象
 */
const currentRightData = ref()
const currentRightNode = ref()
const currentHightLight = ref()// 选中条目的id
const rightMenuPos = ref([0, 0])
function openMenu(event: any, data: any, node: any) {
  let dialogLeft = document.querySelector('.treeContent')?.getBoundingClientRect().left
  let dialogTop = document.querySelector('.treeContent')?.getBoundingClientRect().top

  let left = event.clientX - (dialogLeft ? dialogLeft : 0)
  let top = event.clientY - (dialogTop ? dialogTop : 0)
  showRightMenu.value = true
  rightMenuPos.value = [left, top]
  currentRightData.value = data
  currentRightNode.value = node
  currentHightLight.value = node.id
}

const isFolder = (data: any) => data?.type == '1'
const isInterface = (data: any) => data?.type == '2'
const isModule = (data: any) => data?.type == '3'


/**
 * 当节点被点击时调用的函数。
 *
 * @param data 任何类型的数据，传递给函数的数据参数。
 * @param node 任何类型的节点对象，被点击的节点对象。
 */
function nodeClick(data: any, node: any) {
  // 再次点击相同的项会取消高亮选中状态
  if (currentHightLight.value == node.id) {
    currentHightLight.value = ''
    currentRightData.value = null
  } else {
    currentHightLight.value = node.id
    currentRightData.value = data
  }
}

/**
 * 判断是否允许放置节点
 *
 * @param currentNode 当前节点
 * @param targetNode 目标节点
 * @param c 上下文对象
 * @returns 如果目标节点的类型等于 '1'，则返回 true，否则返回 false
 */
function allowDrop(currentNode: any, targetNode: any, position: any) {
  return (targetNode.data.type == '1' && position == 'inner') ||
    position == 'next' || position == 'prev';
}


/**
 * 处理剪切选项
 *
 * @param moduleItem  
 */
const scissorModuleItem = ref<ModuleInterface | null>(null)
function handleScissorMenu(moduleItem: ModuleInterface) {
  copyModuleItem.value = moduleItem
  scissorModuleItem.value = moduleItem
}


/**
 * 处理复制的逻辑
 *
 * @param moduleItem  
 */
const copyModuleItem = ref<ModuleInterface | null>(null)
function handleCopy(moduleItem: ModuleInterface) {
  copyModuleItem.value = moduleItem
  scissorModuleItem.value = null
}

/**
 * 处理粘贴的函数
 *
 * @returns 无返回值
 */
async function handlePaste(currentModuleItem: ModuleTreeInterface | null) {
  /**
   * 更新菜单排序并重新获取菜单列表
   *
   * @param items 菜单项数组
   */
  const updateSortAndFetch = async (items: Array<ModuleInterface>) => {
    const promiseArr = items.map(updateModule);
    await Promise.all(promiseArr);
    const res = await getModuleList({ appId: props.appId });
    dataSource.value = res.data;
  };

  /**
   * 重置排序并展开树结构
   *
   * @param arr 菜单项数组
   * @param oTreeChildren 树形菜单项数组
   */
  const resetSortAndExpandTree = (arr: Array<ModuleInterface>, oTreeChildren: Array<ModuleTreeInterface>) => {
    oTreeChildren.forEach((item, index) => {
      item.sort = index;
      arr.push(item);
    });
  };

  /**
   * 递归展开树形结构
   *
   * @param pId 父节点的ID
   * @param arr 原始菜单数组
   * @param oTree 待展开的树形结构数组
   * @param pSort 父节点的排序值，默认为-1
   */
  let ms = new Date().getMilliseconds()
  const expandTree = (pId: string | undefined, arr: Array<ModuleInterface>, oTree: Array<ModuleTreeInterface>, pSort = -1) => {
    oTree.forEach((item) => {
      item.parentId = pId;
      item.id = generateRandomString();
      item.name = item.name + (ms + '');
      ms++;
      if (pSort > -1) item.sort = pSort;
      arr.push(item);
      if (item.children && item.children.length) {
        expandTree(item.id, arr, item.children);
      }
    });
  };

  /**
   * 处理粘贴操作
   *
   * @param parentId 父级菜单ID
   * @param sort 排序值
   */
  async function handlePaste(parentId: string | undefined, sort: number) {
    // 剪切并粘贴
    if (scissorModuleItem.value) {
      copyModuleItem.value!.parentId = parentId;
      copyModuleItem.value!.sort = sort;
      await updateModule(copyModuleItem.value);
    } else {
      const oArr: Array<ModuleInterface> = [];
      expandTree(parentId, oArr, [copyModuleItem.value as ModuleTreeInterface], sort);
      const promiseArr = oArr.map(addModule);
      await Promise.all(promiseArr);
    }
    ElMessage.success('粘贴成功');
    getList();
  };

  // 在文件夹下点击粘贴菜单项
  if (currentModuleItem) {
    // 如果当前节点存在子节点，则更新其子节点列表的排序并重新获取菜单列表
    if (currentModuleItem.children && currentModuleItem.children.length) {
      const arr: Array<ModuleInterface> = [];
      resetSortAndExpandTree(arr, currentModuleItem.children);
      await updateSortAndFetch(arr);
    }
    const pId = currentModuleItem.id;
    const sort = currentModuleItem.children?.length || 0;
    await handlePaste(pId, sort);
  } else {
    // 粘贴到最外层菜单项
    const arr: Array<ModuleInterface> = [];
    resetSortAndExpandTree(arr, dataSource.value as ModuleTreeInterface[]);
    await updateSortAndFetch(arr);
    const pId = undefined;
    const sort = dataSource.value?.length || 0;
    await handlePaste(pId, sort);
  }

  scissorModuleItem.value = null;
  copyModuleItem.value = null;
}

// 根据editItem.type返回标题名
const editItemTitle = (editItem: ModuleInterface) => {
  switch (editItem.type) {
    case '1':
      return '文件夹';
    case '2':
      return '接口';
    case '3':
      return '模型';
  }
}


const allModuleList = ref()
/**
 * 获取所有模块列表
 *
 * @returns 无返回值
 */
function getAllModuleList() {
  getAllModule({ appId: props.appId }).then(res => {
    allModuleList.value = res.data
  })
}


watch(() => props.dialogVisible, (val) => {
  visible.value = val
  if (val) {
    getList()
  }
}, {
  immediate: true,
})

watch(() => currentRightData.value, (val) => {
  if (!val) {
    showRightMenu.value = false
  }
}, {
  immediate: true,
  deep: true
})

const moduleActionList = ref<Array<Action>>()
watch(() => editItem.value, (val) => {
  if (isFolder(val)) {
    rules.value.name = [
      { required: true, message: '请输入中文名称', trigger: 'blur', },
    ]
  }
  if (isModule(val)) {
    rules.value.name = [
      { required: true, message: '请输入模型英文名', trigger: 'blur' },
      { pattern: /^[a-zA-Z0-9_-]+$/, message: '只包含字母、数字、_和-', trigger: 'blur' },
    ]
  }
  if (isInterface(val)) {
    rules.value.name = [
      { required: true, message: '请输入接口路径', trigger: 'none' },
      { pattern: /^\/(?!.*\/\/)[a-zA-Z0-9_\/-]+(?<!\/)$/, message: '路径只包含字母、数字、/、_和-，且以/开头，不能以/结尾', trigger: 'blur' },
    ]
    //  获取当前选择的模块对应的方法列表
    let arr = allModuleList.value.filter((item: ModuleInterface) => {
      if (item.name == editItem.value.data.modelName) {
        return true
      }
    })
    if (arr.length > 0) {
      moduleActionList.value = arr[0].data.action
    }
  }
}, {
  immediate: true,
  deep: true
})
</script>
<template>
  <!-- 模型树 -->
  <el-dialog v-model="visible" destroy-on-close append-to-body center title="编辑模型" width="900px" @close="cancel">
    <div class="treeContent">
      <!--  右键菜单 -->
      <right-menu v-model="showRightMenu" :position="rightMenuPos" msg="">
        <template v-slot>
          <div class="menuItemButton" id="rightMenu">
            <el-button class="rightMenuBtn" v-if="isFolder(currentRightData)" icon="Folder" link
              @click.stop="append(currentRightData, '1')">
              添加文件夹
            </el-button>
            <el-button class="rightMenuBtn" v-if="isFolder(currentRightData)" icon="List" link
              @click.stop="append(currentRightData, '3')">
              添加模型
            </el-button>
            <el-button class="rightMenuBtn" v-if="isFolder(currentRightData)" icon="Operation" link
              @click.stop="append(currentRightData, '2')">
              添加接口
            </el-button>
            <el-button class="rightMenuBtn" icon="Edit" link v-if="isModule(currentRightData)"
              @click.stop="editModuleDetail(currentRightData)">模型详情</el-button>
            <el-button class="rightMenuBtn" link icon="EditPen" @click.stop="edit(currentRightData)">基本信息</el-button>
            <el-button class="rightMenuBtn" icon="Delete" link
              @click.stop="remove(currentRightNode, currentRightData)">删除</el-button>
            <el-button class="rightMenuBtn" icon="CopyDocument" link
              @click="handleCopy(currentRightData)">复制</el-button>
            <el-button class="rightMenuBtn" icon="Scissor" link
              @click="handleScissorMenu(currentRightData)">剪切</el-button>
            <el-button class="rightMenuBtn" icon="DocumentChecked"
              v-if="copyModuleItem && copyModuleItem.id && currentRightData?.type == '1'"
              @click="handlePaste(currentRightData)" link>粘贴</el-button>
          </div>
        </template>
      </right-menu>

      <div class="treeContentTop">
        <el-button :icon="Folder" style="margin-bottom:10px;" text type="warning"
          @click.stop="append(currentRightData, '1')" v-show="currentRightData?.type == 1 || !currentRightData">添加文件夹
        </el-button>
        <el-button style="margin-bottom:10px;" :icon="Operation" type="success" link
          @click.stop="append(currentRightData, '2')" v-show="currentRightData?.type == 1 || !currentRightData">添加接口
        </el-button>
        <el-button :icon="List" style="margin-bottom:10px;" text type="primary"
          @click.stop="append(currentRightData, '3')" v-show="currentRightData?.type == 1 || !currentRightData">添加模型
        </el-button>
        <el-button icon="DocumentChecked" v-if="copyModuleItem" style="margin-bottom:10px;" text
          @click.stop="handlePaste(null)">粘贴至顶级
        </el-button>
      </div>
      <el-tree @node-drop="handleNodeDrop" :allow-drop="allowDrop" @node-click="nodeClick" draggable :data="dataSource"
        empty-text="请添加模型" node-key="id" style="margin-top:10px">
        <template #default="{ node, data }">
          <div v-blur="handleTreeBlur" :class="['custom-tree-node', (currentHightLight == node.id) ?
            'is-current' : '',
            (scissorModuleItem && scissorModuleItem.id == data.id) ?
              'scissor' : '']" role="button" tabindex="0" @contextmenu.prevent="openMenu($event, data, node)">
            <span style="display:flex;align-items: center;"
              :class="{ 'isFolder': isFolder(data), 'isModule': isModule(data), 'isInterface': isInterface(data) }">
              <el-icon>
                <Folder v-if="isFolder(data)" />
                <List v-if="isModule(data)" />
                <Operation v-if="isInterface(data)" />
              </el-icon>&nbsp;
              <span>{{ data.label ? data.label : '' }} {{ data.name ? data.name : '' }}</span>
            </span>
          </div>
        </template>
      </el-tree>
    </div>
    <div class="dialog-footer">
      <el-button @click="cancel" type="primary">关闭</el-button>
    </div>
  </el-dialog>
  <!-- 模型信息编辑框 -->
  <el-dialog v-model="visibleEditItem" :title="`编辑${editItemTitle(editItem)}信息`" center width="700px"
    @close="cancelEditItem">
    <div>
      <el-form ref="editModuleDialogRef" :model="editItem" :rules="rules" label-width="100px" @submit.prevent>
        <!-- 文件夹 -->
        <el-form-item label="名称" prop="name" v-if="isFolder(editItem)">
          <el-input v-model="editItem.name" placeholder="请输入中文名称"></el-input>
        </el-form-item>
        <!-- 模型 -->
        <el-form-item label="名称" prop="name" v-if="isModule(editItem)">
          <el-input v-model="editItem.name" placeholder="请输入英文名"></el-input>
        </el-form-item>
        <!-- 接口 -->
        <el-form-item label="路径" prop="name" v-if="isInterface(editItem)">
          <el-input v-model="editItem.name" placeholder="请输入路径,以/开头，如/user/add"></el-input>
        </el-form-item>
        <el-form-item label="模型" prop="data.modelName" v-if="isInterface(editItem)">
          <el-select v-model="editItem.data.modelName" placeholder="请选择模型">
            <el-option v-for="item in allModuleList" :key="item.id" :label="item.name" :value="item.name"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="方法" prop="data.action" v-if="isInterface(editItem)">
          <el-select v-model="editItem.data.action" placeholder="请选择方法">
            <el-option v-for="item in moduleActionList" :key="item.id" :label="item.name"
              :value="item.name"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
    </div>
    <div class="dialog-footer">
      <el-button @click="cancelEditItem">取消</el-button>
      <el-button type="primary" @click="submitEditItem">保存更改</el-button>
    </div>
  </el-dialog>

</template>
<style lang='scss' scoped>
.rightMenuBtn {
  display: flex;
  justify-content: start;
  width: 100%;
  text-align: left;
  border-bottom: 1px solid #e7e7e7 !important;
  border-radius: 0 !important;
  line-height: 30px;
  padding-left: 20px !important;
  margin-left: 0 !important;
  color: rgb(42, 42, 42) !important;
  font-size: 13px;
  vertical-align: baseline;

  &>::deep(.el-button [class*=el-icon]+span) {
    flex-grow: 1;
  }
}

.rightMenuBtn:hover {
  background: #ececec !important;
}

.is-current {
  background-color: #ecf5ff;
}

.scissor {
  filter: opacity(0.4);
  padding-left: 20px !important;

  &::before {
    content: '✂';
    color: rgb(255, 0, 0);
    font-size: 12px;
    position: absolute;
    top: 3px;
    left: 5px;

  }
}

.el-dropdown-link-m {
  padding-top: 4px;
  padding-left: 10px;
  cursor: pointer;
  color: #429dff;
  display: flex;
  align-items: center;
}

.isFolder {
  color: var(--el-color-warning);
}

.isModule {
  color: var(--el-color-primary);

}

.isInterface {
  color: var(--el-color-success);
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 20px;
}

.treeContent {
  width: 100%;
  position: relative;

  &Top {
    border-bottom: 1px solid #ebeef5;
    height: 43px;
  }
}

.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding: 2px 8px 2px 2px;
}
</style>